/*
 * File:   allocator.h
 * Author: aishwarya
 *
 * Created on March 16, 2011, 2:25 PM
 * Implementation of a memory allocator.
 */

#include "allocator.h"

int alloc_init(SAllocator *m, int entries, size_t e_size) {
    m->avail = m->nele = entries;
    m->e_size = e_size;

    m->buf = (char *) calloc(m->nele, m->e_size);
    LOG(LOG_DBG, "Allocated memory buffer from [%p]", m->buf);
    m->reuse = (int *) calloc(m->nele, sizeof (int));
    m->inuse = (int *) calloc(m->nele, sizeof (int));
    m->t = -1;
}

void* alloc_get(SAllocator *m) {
    char *r;

    if (m->avail == 0) {
        LOG(LOG_WARN, "Allocator empty [%p]", m->buf);
        return NULL;
    }

    if (m->t == -1) {
        r = &m->buf[m->e_size * (m->nele - m->avail)];
        m->inuse[m->nele - m->avail] = 1;
    } else {
        LOG(LOG_INFO, "Allocating from Reuse pool [%d]", m->reuse[m->t]);
        r = &m->buf[m->e_size * m->reuse[m->t]];
        m->inuse[m->reuse[m->t]] = 1;
        (m->t)--;
    }

    LOG(LOG_DBG, "Allocated memory @ [%p]", r);

    (m->avail)--;

    return r;
}

int alloc_free(SAllocator *m, void *p) {
    int i, offset;

    offset = (char *) p - (char *) m->buf;

    LOG(LOG_DBG, "Deallocating memory @ [%p] offset = [%p] buffer = [%p]", p, offset, m->buf);

    if (offset < 0) {
        LOG(LOG_WARN, "Invalid offset (%d) in allocator", offset);
        return 0;
    }

    i = offset / (m->e_size);

    if ((int) (i * m->e_size) != offset) {
        LOG(LOG_WARN, "Invalid offset (%d) in allocator", offset);
        return 0;
    }

    if (i >= m->nele) {
        LOG(LOG_WARN, "Invalid index (%d) in allocator", i);
        return 0;
    }

    if (!m->inuse[i]) {
        LOG(LOG_WARN, "Attempt to free same memory (%d) twice", i);
        return 0;
    }

    LOG(LOG_INFO, "Going to free index (%d) in allocator", i);

    m->inuse[i] = 0;
    m->reuse[++(m->t)] = i;
    (m->avail)++;

    return 1;
}

int alloc_count(SAllocator *m) {
    return m->nele;
}

int alloc_avail(SAllocator *m) {
    return m->avail;
}

int alloc_clean(SAllocator *m) {
    if (m->buf)
        free(m->buf);
    if (m->inuse)
        free(m->inuse);
    if (m->reuse)
        free(m->reuse);

    m->t = -1;
    m->avail = m->nele = m->e_size = 0;
}